
#define GL_GLEXT_PROTOTYPES

#include "openglframe.h"
#include <algorithm>

#include "../glDrawFunctions.h"

// Some utility Functions ===================================================================

/// Returns the screen coordinates of a point in world space.
/// @param p input point in screen space
/// @return point in world space
Point worldToScreen(Point p) 
{
    int viewport[4];
    double mvmatrix[16], projmatrix[16], wincoord[3];
    glGetIntegerv (GL_VIEWPORT, viewport);
    glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
    glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
    gluProject (p.x(), p.y(), p.z(),
               mvmatrix, projmatrix, viewport,
               &wincoord[0], &wincoord[1], &wincoord[2]);
    return Point (wincoord[0], wincoord[1], wincoord[2]);
}

/// Returns the world coordinates of a point in screen space.
/// @param p input point in world space
/// @return point in screen space
Point screenToWorld (Point p) 
{
    int viewport[4];
    double mvmatrix[16], projmatrix[16], wldcoord[3];
    glGetIntegerv (GL_VIEWPORT, viewport);
    glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
    glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
    gluUnProject (p.x(), p.y(), p.z(),
                  mvmatrix, projmatrix, viewport,
                  &wldcoord[0], &wldcoord[1], &wldcoord[2]);
    return Point (wldcoord[0], wldcoord[1], wldcoord[2]);
}


// OpenGLFrame methods ====================================================================

/// Constructor.
/// @param parent widget.
/// @param shareWidget widget sharing the same opengl context.
/// @param f Qt window flags.
OpenGLFrame::OpenGLFrame (QWidget* parent, const QGLWidget* shareWidget, Qt::WFlags f)
   : QGLWidget (parent, shareWidget, f), 
     sceneTransform(), arcball(1,1) {
        
   // for keyboard use 
   setFocusPolicy(Qt::StrongFocus);
   
   sceneTransform = Transformation(CGAL::IDENTITY);
   cursor3D = Point(0,0,0);
   interactionMode = SELECT_MODE;
   
   fovy = 40.0;
   zoomFactor = 1.0;
   meshrendermode_mask = 0x20;
   rigskin_mask = 0x08;
   
   //~ cout<<":: Loaging the floor ..."<<endl;
   //~ floor.loadShape("../data/floorT.off");
   //~ cout<<":: Loaging the floor ... done! "<<endl;
   
   model = 0;
   
   isProjOrtho = true; // orthographic projection
   is_lightingOn = true;
   
   viewitems_mask = 0x0; 
   viewitems_mask = viewitems_mask | MODELDEF;
   viewitems_mask = viewitems_mask | SKELETONDEF;
   viewitems_mask = viewitems_mask | KEYPOINTS;
   viewitems_mask = viewitems_mask | CONSTRAINTS;
   //~ viewitems_mask = viewitems_mask | FLOOR;
   viewitems_mask = viewitems_mask | INFO;
   
   Vector displacement = cursor3D - Point(0,0,0);
   
   skelEditOperation = NONE;
   
   isViewClosestJoint = false;
   isViewAssociations = false;
   isViewBoneVertices = false;
   isViewVertexBones = false;
   isViewJointVertices = false;
   isViewJointVertice = false;
   isViewVertexJoints = false;
   isViewVertexJoint = false;
   isViewClusters = false;
   clustering_using_bones = true;
   
   generateColors();
   
   cout<<"OpenGLFrame::OpenGLFrame()"<<endl;
}

OpenGLFrame::~OpenGLFrame() {
   
   if (model!=0)        delete model;
}

/// Sets the interaction mode
void OpenGLFrame::setInteractionMode (InteractionMode m) {
   interactionMode = m;
}

bool OpenGLFrame::loadShapeOFF(const QString& filename) {
   
   MeshWithSkeleton *newmodel = MeshWithSkeleton::load_off(filename.toStdString().c_str());
   if (newmodel) {
      if (model != 0) delete model;
      model = newmodel;
      genWorldLimits();
      setGLview();
      
      sceneTransform = Transformation(CGAL::IDENTITY); /// reset the transformation matrix
      
      return true;
   }
   return false;
}

bool OpenGLFrame::saveShapeOFF(const QString& filename) {
   
   if (model) { 
      model->saveShapeOFF(filename.toStdString().c_str());
      return true;
   }
   return false;
}

bool OpenGLFrame::saveAsSkeleton(const QString& filename) {
   
   if (model==0) return false;
   if (model->skeleton) {
      model->skeleton->saveAs(filename.toStdString().c_str());
      return true;
   }
   return false;
}

bool OpenGLFrame::loadSkeleton(const QString& filename) {
   
   if (model==0) return false;
   
   Skeleton *newskeleton = Skeleton::load(filename.toStdString().c_str());
   if (newskeleton) {
      if (model->skeleton != 0) delete model->skeleton;
      model->skeleton = newskeleton;
      return true;
   }
   return false;
}

bool OpenGLFrame::loadSkinning(const QString& filename) {
   
   if (model==0) return false;
   if (filename.contains(".sj"))
      model->loadSkinningOnJoints(filename.toStdString().c_str());
   else if (filename.contains(".sb"))
      model->loadSkinningOnBones(filename.toStdString().c_str());
   return true;
}

bool OpenGLFrame::saveSkinning(const QString& filename) {
   
   if (model==0) return false;
   if (filename.contains(".sj"))
      model->saveSkinningOnJoints(filename.toStdString().c_str());
   else if (filename.contains(".sb"))
      model->saveSkinningOnBones(filename.toStdString().c_str());
   return true;
}

void checkingGLerrors(const char *label) {
   
   GLenum errCode;
   const GLubyte *errStr;
   if ((errCode = glGetError()) != GL_NO_ERROR) {
      errStr = gluErrorString(errCode);
      printf("OpenGL ERROR: ");
      printf((char*)errStr);
      printf("(Label: ");
      printf(label);
      printf(")\n.");
   }
}

/// OpenGL initialization.
void OpenGLFrame::initializeGL() {
   
   glClearColor(1,1,1,0);
   glEnable(GL_LIGHTING);
   glEnable(GL_LIGHT0);
   glEnable(GL_LIGHT1);
   glEnable(GL_DEPTH_TEST);
   glEnable(GL_NORMALIZE);
   glShadeModel(GL_SMOOTH);
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   
   // lighting setup
   GLfloat left_light_position[] =  {0.0, 2.0, 2.0, 0.0};
   GLfloat right_light_position[] = {0.0, -2.0, 2.0, 0.0};
   glLightfv(GL_LIGHT0, GL_POSITION, left_light_position);
   glLightfv(GL_LIGHT1, GL_POSITION, right_light_position);
   GLfloat left_diffuse_light[] =  {1.0f, 1.0f, 1.0f, 1.0f};
   GLfloat right_diffuse_light[] = {1.0f, 1.0f, 1.0f, 1.0f};
   glLightfv(GL_LIGHT0, GL_DIFFUSE, left_diffuse_light);
   glLightfv(GL_LIGHT1, GL_DIFFUSE, right_diffuse_light);
   
   genWorldLimits();
}

/// Performs a glMultMatrix where the matrix is a CGAL transformation.
/// @param t CGAL tranformation matrix.
void glTransform (const Transformation &t)
{
   double matrix [] = { t.m(0,0), t.m(1,0), t.m(2,0), 0,
                        t.m(0,1), t.m(1,1), t.m(2,1), 0,
                        t.m(0,2), t.m(1,2), t.m(2,2), 0,
                        t.m(0,3), t.m(1,3), t.m(2,3), 1 };
   glMultMatrixd (matrix);
}

/// A simple opengl drawing callback.
void OpenGLFrame::paintGL()
{
   setGLview();
   
   glClearStencil(0x1);
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   gluLookAt(cameye[0],cameye[1],cameye[2],camcenter[0],camcenter[1],camcenter[2],0,1,0);
   
   glTransform(sceneTransform);
   
   if (model==0) return;
   
   // Draw the floor
   if ((viewitems_mask & FLOOR) == FLOOR)
      drawFloor();
   
    // Draw the model
   if ((viewitems_mask & MODELDEF) == MODELDEF) {
      
      glEnableClientState(GL_VERTEX_ARRAY);
      glEnableClientState(GL_NORMAL_ARRAY);
      typedef Mesh::Vertex Vertex;
      Vertex_iterator vi = model->vertices_begin();
      glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &(vi->point()[0]));
      glNormalPointer(GL_FLOAT, sizeof(Vertex), &(vi->normal()[0]));
      
      static GLfloat colrf[4] = {0.0f, 0.5f, 1.0f, 1.0f };
      static GLfloat colrb[4] = {1.0f, 0.0f, 0.0f, 1.0f };
      glMaterialfv(GL_FRONT, GL_DIFFUSE, colrf);
      glMaterialfv(GL_BACK, GL_DIFFUSE, colrb);
      
      glLineWidth(1.0);
      (is_lightingOn)? glEnable(GL_LIGHTING):glDisable(GL_LIGHTING); 
      glDrawModel(model, meshrendermode_mask);
      
      //~ glDisableClientState(GL_VERTEX_ARRAY);
      //~ glDisableClientState(GL_NORMAL_ARRAY);
      
      glDrawRiggingSkinning(model, rigskin_mask, clustering_using_bones);
      
   }
   
   // Render Shadows
   if ((viewitems_mask & FLOOR) == FLOOR) {
      glDisable(GL_LIGHTING);
      glColor4f(0.0f, 0.0f, 0.0f, 0.8);
      glPushMatrix();
      float l[] = {2.0, 50.0, 30.0}; // Coordinates of the light source
      float n[] = {0.0, -1.0, 0.0}; // Normal vector for the plane
      float e[] = {0.0, -0.8, 0.0}; // Point of the plane
      glShadowProjection(l,n,e);
      //~ glDrawModel(model, FLAT);
         glShadeModel(GL_FLAT);
         glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
         glShadeModel(GL_SMOOTH);
      glPopMatrix();
      glEnable(GL_LIGHTING);
   }
   
   
   /*
   {
   float *p0 = &model->vpositions[1274*3];
   float *p1 = &model->vpositions[1284*3];
   float *p2 = &model->vpositions[618*3];
   float *p3 = &model->vpositions[775*3];
   float *p4 = &model->vpositions[1679*3];
   glPointSize(8.0);
   glDisable(GL_LIGHTING);
      glBegin(GL_POINTS);
      glColor4f(1.0,0.0,0.0,1.0);
      glVertex3f(p0[0], p0[1], p0[2]);
      glColor4f(0.0,1.0,0.0,1.0);
      glVertex3f(p1[0], p1[1], p1[2]);
      glColor4f(0.0,0.0,1.0,1.0);
      glVertex3f(p2[0], p2[1], p2[2]);
      glColor4f(0.0,0.0,0.0,1.0);
      glVertex3f(p3[0], p3[1], p3[2]);
      glColor4f(1.0,0.0,1.0,1.0);
      glVertex3f(p4[0], p4[1], p4[2]);
      glEnd();
      
      glColor4f(1.0,0.0,0.0,1.0);
      glPointSize(5.0);
      glBegin(GL_POINTS);
      //~ cout<<"model->listavisitados.size(): "<<model->listavisitados.size()<<endl;
      for (int i=0; i<model->listavisitados.size(); ++i) {
         float *p = &model->vpositions[model->listavisitados[i]*3];
         glVertex3f(p[0], p[1], p[2]);
      }
      glEnd();
      
   glEnable(GL_LIGHTING);
   }
   */
   
   if (skelEditOperation == BONENEW) {
      glDisable(GL_DEPTH_TEST);
      glDisable(GL_LIGHTING);
      glEnable(GL_LINE_STIPPLE);
      glLineStipple(1, 0xF0F0);
      glLineWidth(4.0);
      glColor4f(0.0,0.0,0.0,1.0);
      glBegin(GL_LINES);
      glVertex3f(pNewJoinSource[0], pNewJoinSource[1], pNewJoinSource[2]);
      glVertex3f(pNewJoinTarget[0], pNewJoinTarget[1], pNewJoinTarget[2]);
      glEnd();
      glDisable(GL_LINE_STIPPLE);
      glEnable(GL_LIGHTING);
      glEnable(GL_DEPTH_TEST);
   }
   
   // Draw the 3D cursor
   
   //~ // Draw the skeleton
   if ((viewitems_mask & SKELETONDEF) == SKELETONDEF) {
      glDisable(GL_DEPTH_TEST);
      glDrawSkel(model->skeleton);
      glEnable(GL_DEPTH_TEST);
   }
   
   //~ if (isViewClusters) {
      //~ drawMeshVerticesANDitsRiggingGroup(model);
   //~ }
   
   //~ if (isViewClosestJoint) {
      //~ drawMeshVerticesANDitsRiggingGroup(model);
   //~ }
   //~ if (isViewAssociations) {
      
   //~ }
   
   /// 
   if (isViewVertexJoints) {
      drawOneMeshVertexANDitsJoints(model, idSelected);
   }
   if (isViewVertexJoint) {
      drawOneMeshVertexANDitsJoint(model, idSelected);
   }
   if (isViewJointVertices) {
      drawOneJointANDitsVertices(model, idSelected);
   }
   if (isViewJointVertice) {
      drawOneJointANDitsVertice(model, idSelected);
   }
   //~ if (isViewBoneVertices) {
      //~ drawOneBoneANDitsVertices(model, idSelected);
   //~ }
   //~ if (isViewVertexBones) {
      //~ drawOneMeshVertexANDitsBones(model, idSelected);
   //~ }
   
   
   // Render info
   if ((viewitems_mask & INFO) == INFO) {
      glDisable(GL_LIGHTING);
      glDisable(GL_DEPTH_TEST);
      
         frame++;
         std::time(&end);
         double ddifftime = std::difftime(end, start);
         if (ddifftime > 1) {
            cfps = frame/ddifftime;
            start = end;
            frame = 0;
         }
      
      //~ updateFps(time.elapsed());
      QString strInfoNv=QString("vertices: %1").arg(model->nvertices());
      QString strInfoNf=QString("faces: %1").arg(model->nfaces());
      //~ QString strInfoNj=QString("joints: %1").arg(model->skeleton->nJoints());
      //~ QString strInfoNcp=QString("control points: %1").arg(mls_def.getNctrlpoints());
      QString strInfoFPS=QString("FPS: %1").arg(cfps,7,'f',1);
      
      glColor4f(0.0, 0.0, 0.0, 1.0);
      renderText(30,15, strInfoNv);
      renderText(30,30, strInfoNf);
      //~ renderText(30,45, strInfoNj);
      //~ renderText(30,60, strInfoNcp);
      renderText(30,75, strInfoFPS);
      
      glEnable(GL_LIGHTING);
      glEnable(GL_DEPTH_TEST);
   }
   
}

void OpenGLFrame::drawFloor() {
   
   /// Draw using vertex arrays
   //~ glEnableClientState(GL_VERTEX_ARRAY);
   //~ glVertexPointer(3, GL_FLOAT, 0, &floor.vpositions[0]);
   /*
   //~ glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT | GL_LINE_BIT | GL_LIGHTING_BIT);
   glColor4f(0.0,0.3,0.0,0.8);
   glLineWidth(1.0);
   glDisable(GL_LIGHTING);
   glEnable(GL_POLYGON_OFFSET_FILL);
   glPolygonMode(GL_FRONT, GL_FILL);
   glPolygonMode(GL_BACK, GL_LINE);
   glPolygonOffset(1,1);
   //~ glDrawElements(GL_TRIANGLES, 3*floor.nfaces(), GL_UNSIGNED_INT, &floor.faces[0]);
   glDrawModel(&floor);
   glDisable(GL_POLYGON_OFFSET_FILL);
   
  
   glColor4f(0.0,0.1,0.0,1.0);
   glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
   glDrawModel(&floor);
   
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   glEnable(GL_LIGHTING);
   //~ glPopAttrib();
   glColor4f(0.0f, 0.5f, 1.0f, 1.0f);
   */
   
   glPolygonMode(GL_FRONT, GL_FILL);
   glPolygonMode(GL_BACK, GL_LINE);
   glDisable(GL_LIGHTING);
   glEnable(GL_POLYGON_OFFSET_FILL);
   glPolygonOffset(1,1);
   glColor3f(0.1,0.5,0.0);
   glBegin(GL_QUADS);
   glNormal3f(0.0,1.0,0.0);
   glVertex3f(-2.5,-1.0, 2.5);
   glVertex3f( 2.5,-1.0, 2.5);
   glVertex3f( 2.5,-1.0,-2.5);
   glVertex3f(-2.5,-1.0,-2.5);
   glEnd();
   glEnable(GL_LIGHTING);
   glDisable(GL_POLYGON_OFFSET_FILL);
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
   //~ glColor4f(0.0f, 0.5f, 1.0f, 1.0f);
}

// This function is from http://www.devmaster.net community.
void OpenGLFrame::glShadowProjection(float *l, float *e, float *n) {
   
   float d, c;
   float mat[16];
   
   // These are c and d (corresponding to the tutorial)
   d = n[0]*l[0] + n[1]*l[1] + n[2]*l[2];
   c = e[0]*n[0] + e[1]*n[1] + e[2]*n[2] - d;
   
   // Create the matrix. OpenGL uses column by column
   // ordering
   mat[0]  = l[0]*n[0]+c;
   mat[4]  = n[1]*l[0];
   mat[8]  = n[2]*l[0];
   mat[12] = -l[0]*c-l[0]*d;
  
   mat[1]  = n[0]*l[1];
   mat[5]  = l[1]*n[1]+c;
   mat[9]  = n[2]*l[1];
   mat[13] = -l[1]*c-l[1]*d;
  
   mat[2]  = n[0]*l[2];
   mat[6]  = n[1]*l[2];
   mat[10] = l[2]*n[2]+c;
   mat[14] = -l[2]*c-l[2]*d;
  
   mat[3]  = n[0];
   mat[7]  = n[1];
   mat[11] = n[2];
   mat[15] = -d;

   // Finally multiply the matrices together *plonk*
   glMultMatrixf(mat);
}

/// It computes the correct world limits (used in the openglWorld definition)
/// It's computed in function of current model's BB 
/// It avoids distorsion on visualization
void OpenGLFrame::genWorldLimits() {
   
   if (model != 0) {
      mincoo[0] = model->mincoo[0]; mincoo[1] = model->mincoo[1]; mincoo[2] = model->mincoo[2];
      maxcoo[0] = model->maxcoo[0]; maxcoo[1] = model->maxcoo[1]; maxcoo[2] = model->maxcoo[2];
   }
   else {
      mincoo[0] = -1; mincoo[1] = -1; mincoo[2] = -1;
      maxcoo[0] =  1; maxcoo[1] =  1; maxcoo[2] =  1;
   }
   
   float xc = mincoo[0] + 0.5*(maxcoo[0]-mincoo[0]);
   float yc = mincoo[1] + 0.5*(maxcoo[1]-mincoo[1]);
   
   float WorldWidth  = abs(maxcoo[0]-mincoo[0]);
   float WorldHeight = abs(maxcoo[1]-mincoo[1]);
   
   if ((WorldHeight/WorldWidth) > ((float)height()/(float)width())) {
      WorldWidth = WorldHeight / ((float)height()/(float)width());
   }
   else if ((WorldHeight/WorldWidth) < ((float)height()/(float)width())) {
      WorldHeight = WorldWidth * ((float)height()/(float)width());
   }
   
   // world limits
   mincoo[0] = xc - 0.5*WorldWidth;
   mincoo[1] = yc - 0.5*WorldHeight;
   
   maxcoo[0] = xc + 0.5*WorldWidth;
   maxcoo[1] = yc + 0.5*WorldHeight;
   
   // center camera
   camcenter[0] = xc;
   camcenter[1] = yc;
   camcenter[2] = mincoo[2] + 0.5*(maxcoo[2]-mincoo[2]);
}

/// It sets the projection/modelview matrices
void OpenGLFrame::setGLview() {
   
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   
   float WorldWidth  = abs(maxcoo[0]-mincoo[0])*0.5;
   float WorldHeight = abs(maxcoo[1]-mincoo[1])*0.5;
   
   float objDistance = 0.8660*(maxcoo[1]-mincoo[1]); /// sqrt(3)/2 = 0.8660
   //~ float objDistance = WorldHeight;
   float nearplane = objDistance - objDistance*0.5;
   float farplane  = nearplane + 20.0;
   
   cameye[0] = camcenter[0];
   cameye[1] = camcenter[1];
   cameye[2] = maxcoo[2] + objDistance;
   
   if (isProjOrtho) 
      glOrtho(-WorldWidth*zoomFactor,  WorldWidth*zoomFactor, 
              -WorldHeight*zoomFactor, WorldHeight*zoomFactor, 
               nearplane, farplane);
   else 
      gluPerspective(fovy*zoomFactor, (GLfloat)width()/(GLfloat)height(), nearplane, farplane);
   
   
   //~ glMatrixMode(GL_PROJECTION);
   //~ glLoadIdentity();
   //~ glOrtho(-0.15, 0.15, -0.15, 0.15, -2.0, 2.0);
   
   glMatrixMode(GL_MODELVIEW);
   glLoadIdentity();
   glViewport(0.0, 0.0, width(), height());
}
   
/// A simple resize callback.
/// @param width the width of the new window.
/// @param height the height of the new window.
void OpenGLFrame::resizeGL(int width, int height) 
{
    //~ int side = width < height ? width : height;
    //~ glMatrixMode (GL_PROJECTION);
    //~ glLoadIdentity ();
    //~ glOrtho (-1.0 * width/side, 1.0 * width/side, 
         //~ -1.0 * height/side, 1.0 * height/side, 10, -10);
    //~ glMatrixMode (GL_MODELVIEW);
    //~ glViewport(0, 0, width, height);
   
   checkingGLerrors("resizeGL()");
   
   setGLview();
   arcball.setBounds((GLfloat)width, (GLfloat)height);  //Update mouse bounds for the arcball
}

Vector OpenGLFrame::getTranslationWorld(int x, int y, int xlast, int ylast) {
   
   //~ cout<<":: "<<x<<" "<<y<<" "<<xlast<<" "<<ylast<<" "<<endl;
   
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetIntegerv(GL_VIEWPORT, viewport);
   
   GLdouble m[]={1,0,0,0,
              0,1,0,0,
              0,0,1,0,
              0,0,0,1};
   
   double wbx, wby, wbz, wex, wey, wez;
   gluUnProject(xlast, ylast, 0.0, mMtx, pMtx, viewport, &wbx, &wby, &wbz);
   gluUnProject(x, y, 0.0, mMtx, pMtx, viewport, &wex, &wey, &wez);
   return Vector(wex-wbx, wey-wby, wez-wbz);
              
   //~ gluUnProject(xlast, ylast, 0.0, m, pMtx, viewport, &wbx, &wby, &wbz);
   //~ gluUnProject(x, y, 0.0, m, pMtx, viewport, &wex, &wey, &wez);
   //~ return Vector(wex-wbx, wey-wby, 0);
}

Point OpenGLFrame::getPointWorld(int x, int y) {
   
   glGetDoublev(GL_MODELVIEW_MATRIX, mMtx);
   glGetDoublev(GL_PROJECTION_MATRIX, pMtx);
   glGetIntegerv(GL_VIEWPORT, viewport);
   
   //~ cout<<"x,y: "<<x<<" "<<y<<endl;
   
   //~ GLdouble m[]={1,0,0,0,
                 //~ 0,1,0,0,
                 //~ 0,0,1,0,
                 //~ 0,0,0,1};
   
   double wx, wy, wz;
   gluUnProject(x, y, 0.0, mMtx, pMtx, viewport, &wx, &wy, &wz);
   //~ gluUnProject(x, y, 1.0, m, pMtx, viewport, &wx, &wy, &wz);
   
   //~ cout<<"wx,wy,wz: "<<wx<<" "<<wy<<" "<<wz<<endl;
   
   return Point(wx, wy, wz);
}

/// Callback for handling mouse button press.
/// @param event encapsulates the event's info.
void OpenGLFrame::mousePressEvent(QMouseEvent *event) 
{
   xini=xlast=event->x();
   yini=ylast=event->y();
   list<uint> selected;
   
   // Do a different thing depending on interactionMode
   switch (interactionMode) {
      case SELECT_MODE: {
         cout<<"SELECT_MODE"<<endl;
         
         // See which object was clicked
         //~ if (!(event->modifiers() & Qt::ShiftModifier)) {
            //~ selected.clear();
            //~ group.clear();
         //~ }
         //~ int isel = pickObject (event->x(), event->y());
         //~ selection.begin(event->x(),height()-event->y());
         //~ paintGLselection();
         //~ selection.end(selected);
         
         if (isViewVertexJoints or isViewVertexJoint) {
            cout<<"aaaa if"<<endl;
            // select a vertex
            selection.begin(event->x(),height()-event->y());
            GLdrawVerticesForSelection(model);
            selection.end(selected);
            
            if (not selected.empty()) idSelected = selected.front();
         }
         else if (isViewJointVertices or isViewJointVertice) {
            cout<<"aaaa else if 1"<<endl;
            // select a joint
            selection.begin(event->x(),height()-event->y());
            GLdrawVerticesForSelection(model->skeleton);
            selection.end(selected);
            
            if (not selected.empty()) idSelected = selected.front();
         }
         if (event->button() == Qt::LeftButton) {
            /*
            cout<<"aaaa else if 2"<<endl;
            selection.begin(event->x(),height()-event->y(),2,2);
            GLdrawFacesForSelection(model);
            selection.end(selected);
            if (selected.size() > 1) {
               
               if (model->skeleton == 0) model->skeleton = new Skeleton;
               
               unsigned idf = selected.front();
               unsigned idb = selected.back();
               const uint *ff = &model->faces[idf*3]; // front face 
               const uint *fb = &model->faces[idb*3]; // back face
               Vertex_iterator vi = model->vertices_begin();
               Vertex_handle seedf = vi[ff[0]].halfedge()->vertex();
               Vertex_handle seedb = vi[fb[0]].halfedge()->vertex();
               float *pf = &model->vpositions[seedf->id()];
               float *pb = &model->vpositions[seedb->id()];
               
               //~ Point p = getPointWorld(event->x(), height()-event->y());
               //~ p = Point(p[0], p[1], 0.5*(pf[2]+pb[2]));
               //~ idSelected = model->skeleton->newJoint(Point3(p[0], p[1], 0.5*(pf[2]+pb[2])));
               /// //~ newSkelBone(idNewJoinSource, idSelected);
               
               Point p(0.5*(pf[0]+pb[0]), 0.5*(pf[1]+pb[1]), 0.5*(pf[2]+pb[2]));
               idSelected = model->skeleton->newJoint(Point3(0.5*(pf[0]+pb[0]), 0.5*(pf[1]+pb[1]), 0.5*(pf[2]+pb[2])));
               
               idNewJoinSource = idSelected;
               pNewJoinSource = Point(p[0],p[1],p[2]);
               pNewJoinTarget = Point(p[0],p[1],p[2]);
               
               skelEditOperation = BONENEW;
               interactionMode = EDITSKEL_MODE;
            }
            */
         }
         else if (event->button() == Qt::MidButton) { //LeftButton
            // Left button rotates the object
            arcball.click(event->x(),height()-event->y());
            objectToScene = sceneTransform;
            //~ glDraw();
         }
         else if (event->button() == Qt::RightButton) {
            // Right button translates
            grabStart = Point(event->x(), height()-event->y(), 0);
            pixelDisplacement[0] = screenToWorld(grabStart + Vector(1,0,0)) - screenToWorld(grabStart);
            pixelDisplacement[1] = screenToWorld(grabStart + Vector(0,1,0)) - screenToWorld(grabStart);
            objectToScene = sceneTransform;
         }
         event->accept(); 
         glDraw();
         break; // End of case SELECT_MODE
      }
      case EDITSKEL_MODE: {
         if (skelEditOperation == BONENEW) {
            selection.begin(event->x(),height()-event->y());
            GLdrawVerticesForSelection(model->skeleton);
            selection.end(selected);
            
            if (not selected.empty()) {
               idSelected = selected.front();
               //~ pressedbutton |= LEFTBUTTONPRESS;
               newSkelBone(idNewJoinSource, idSelected);
               skelEditOperation = NONE;
               interactionMode = SELECT_MODE;
            }
            else {
               selection.begin(event->x(),height()-event->y(),2,2);
               GLdrawFacesForSelection(model);
               selection.end(selected);
               if (selected.size() > 1) {
                  unsigned idf = selected.front();
                  unsigned idb = selected.back();
                  const uint *ff = &model->faces[idf*3]; // front face 
                  const uint *fb = &model->faces[idb*3]; // back face
                  Vertex_iterator vi = model->vertices_begin();
                  Vertex_handle seedf = vi[ff[0]].halfedge()->vertex();
                  Vertex_handle seedb = vi[fb[0]].halfedge()->vertex();
                  //~ float *pf = &model->vpositions[seedf->id()];
                  //~ float *pb = &model->vpositions[seedb->id()];
                  const Point3 &pf = seedf->point();
                  const Point3 &pb = seedb->point();
                  
                  //~ Point p = getPointWorld(event->x(), height()-event->y());
                  //~ p = Point(p[0], p[1], 0.5*(pf[2]+pb[2]));
                  //~ idSelected = model->skeleton->newJoint(Point3(p[0], p[1], 0.5*(pf[2]+pb[2])));
                  
                  Point p(0.5*(pf[0]+pb[0]), 0.5*(pf[1]+pb[1]), 0.5*(pf[2]+pb[2]));
                  idSelected = model->skeleton->newJoint(Point3(0.5*(pf[0]+pb[0]), 0.5*(pf[1]+pb[1]), 0.5*(pf[2]+pb[2])));
                  
                  newSkelBone(idNewJoinSource, idSelected);
                  
                  idNewJoinSource = idSelected;
                  pNewJoinSource = Point(p[0],p[1],p[2]);
                  pNewJoinTarget = Point(p[0],p[1],p[2]);
               }
               else {
                  skelEditOperation = NONE;
                  interactionMode = SELECT_MODE;
                  model->skeleton->buildGraph();
               }
            }
         }
         else if (skelEditOperation == JOINMOVE) {
            skelEditOperation = NONE;
            //~ interactionMode = SELECT_MODE;
         }
      }
   }
   event->accept(); 
   glDraw();
}

/// Callback for handling mouse movements.
/// @param event encapsulates the event's info.
void OpenGLFrame::mouseMoveEvent(QMouseEvent *event)
{
   switch (interactionMode) {
      case SELECT_MODE: {
         if (event->button() == Qt::LeftButton) { //
            
         }
         else if ((event->buttons() & Qt::MidButton) ) {
            // Drag with left button rotates the object.
            // Pass the mouse movement to the arcball.
            /// arcball->mouseDrag(event->x(),height()-event->y());
            arcball.drag(event->x(),height()-event->y(),tM);
            Transformation mTransf(tM[0], tM[4], tM[8],  tM[12],
                                   tM[1], tM[5], tM[9],  tM[13],
                                   tM[2], tM[6], tM[10], tM[14]);
             
            // Compose transformation with that of the arcball.
            sceneTransform = mTransf*objectToScene;
            //~ glDraw();
         }
         else if (event->buttons() & Qt::RightButton) {
            // Translation
            Point grabEnd(event->x(), height() - event->y(), 0);
            Vector displacement = (grabEnd-grabStart);
            Vector worldDisplacement = pixelDisplacement[0] * displacement[0] + pixelDisplacement[1]*displacement[1];
            
            sceneTransform = objectToScene * Transformation(CGAL::TRANSLATION, worldDisplacement);
            //~ glDraw();
         }
         //~ break;
      }
      case EDITSKEL_MODE: {
         if (skelEditOperation == BONENEW) {
            int x=event->x(), y=height()-event->y();
            
            //~ Vector t = getTranslationWorld(x,y,xlast,height()-ylast);
            //~ Point &p = pNewJoinTarget;
            //~ pNewJoinTarget = Point(p[0]+t[0], p[1]+t[1], p[2]);
            
            Point p = getPointWorld(x,y);
            pNewJoinTarget = Point(p[0], p[1], pNewJoinTarget[2]);
         }
         else if (skelEditOperation == JOINMOVE) {
            int x=event->x(), y=height()-event->y();
            Vector t = getTranslationWorld(x,y,xlast,height()-ylast);
            if (t*t == 0)  return;
            model->skeleton->translateJoint(idSelected, Vector3(t[0],t[1],t[2]));
         }
         //~ break;
      }
      default: {}
   }
   
   xlast=event->x();
   ylast=event->y();
   
   event->accept(); 
   glDraw();
}

/// Callback for handling mouse button release.
/// @param event encapsulates the event's info.
void OpenGLFrame::mouseReleaseEvent(QMouseEvent *event)
{
   switch (interactionMode) {
      case SELECT_MODE: {
         //~ break;
      }
      //~ case ANIMATE_MODE: {
         //~ break;
      //~ }
   }
   
   event->accept(); 
   //~ glDraw();
}

/// Inplements zoom operations using the mouse wheel
void OpenGLFrame::wheelEvent(QWheelEvent *e) {
   
   //~ if (e->delta()>0) zoomFactor+= 0.1;
   //~ else              zoomFactor-= 0.1;
   //~ e->accept(); 
   //~ setGLview();
   //~ glDraw();
   
   if (e->delta()>0) zoomIn();
   else              zoomOut();
      
   e->accept(); 
}

void OpenGLFrame::keyPressEvent(QKeyEvent *event) {
   
   if (event->key() == Qt::Key_Escape) {
      exit(0);
   }
}

void OpenGLFrame::contextMenuEvent(QContextMenuEvent *e) {
   
   if (model == 0) return;
   
   cout<<"contextMenuEvent()"<<endl;
   
   if (interactionMode==EDITSKEL_MODE) {
      list<uint> selected;
      if (model->skeleton == 0) {
         // verificamos se um mesh vertex foi clicked
         selection.begin(e->x(),height()-e->y());
         GLdrawFacesForSelection(model);
         selection.end(selected);
         if (selected.size() > 1) {
            // 
            unsigned idf = selected.front();
            unsigned idb = selected.back();
            const uint *ff = &model->faces[idf*3]; // front face 
            const uint *fb = &model->faces[idb*3]; // back face
            Vertex_iterator vi = model->vertices_begin();
            Point3 pf = model->getVertexPosition(vi[ff[0]].halfedge()->vertex());
            Point3 pb = model->getVertexPosition(vi[fb[0]].halfedge()->vertex());
            Point3 p(0.5*(pf[0]+pb[0]), 0.5*(pf[1]+pb[1]), 0.5*(pf[2]+pb[2]));
            pNewJoinSource = p;
            
            QMenu menu(this);
            menu.addAction("New Joint", this, SLOT(newSkelJoint()));
            menu.exec(e->globalPos());
            
            e->accept(); 
            glDraw();
            return;
         }
      }
      
      // verificamos se um joint foi clicked
      selection.begin(e->x(),height()-e->y());
      GLdrawVerticesForSelection(model->skeleton);
      selection.end(selected);
      if (not selected.empty()) {
         idSelected = selected.front();
         QMenu menu(this);
         menu.addAction("Move Joint", this, SLOT(moveSkelJoint()));
         menu.addAction("Delete Joint", this, SLOT(deleteSkelJoint()));
         menu.addAction("New Bone", this, SLOT(newSkelBoneSourceJoint()));
         menu.exec(e->globalPos());
      }
      else { // verificamos se um bone foi clicked
         selection.begin(e->x(),height()-e->y());
         GLdrawBonesForSelection(model->skeleton);
         selection.end(selected);
         if (selected.empty())   return;
         
         idSelected = selected.front();
         QMenu menu(this);
         //~ menu.addAction("Bind Vertices", this, SLOT(bindSkelBoneVertices()));
         menu.addAction("Split Bone", this, SLOT(splitSkelBone()));
         menu.addAction("Delete Bone", this, SLOT(deleteSkelBone()));
         menu.addAction("Flip Bone", this, SLOT(flipSkelBone()));
         menu.exec(e->globalPos());
         
      }
   }
   
   e->accept(); 
   glDraw();
}

/// Implements the "zoomin" operation. Narrows the view of the scene.
void OpenGLFrame::zoomIn ()
{
   sceneTransform = Transformation (CGAL::SCALING, 1.2) * sceneTransform;
   glDraw();
}

/// Implements the "zoomout" operation. Enlarges the view of the scene.
void OpenGLFrame::zoomOut ()
{
   sceneTransform = Transformation (CGAL::SCALING, 1.0/1.2) * sceneTransform;
   glDraw();
}

/// Implements the "zoomAll" operation
/// Computes a scene viewing transformation so that all objects are shown.
void OpenGLFrame::zoomAll ()
{
   if (model == 0) {
      // A simple identity transformation suffices for exhibiting 0 objects
      sceneTransform = Transformation(CGAL::IDENTITY);
   } else {
      float *pmin = model->mincoo;
      float *pmax = model->maxcoo;
      Vector3 v(pmin[0]-pmax[0], pmin[1]-pmax[1], pmin[2]-pmax[2]);
      double d = sqrt(v*v);
      if (d == 0) d = 1.0;
      Point c((pmin[0]-pmax[0])*0.5, (pmin[1]-pmax[1])*0.5, (pmin[2]-pmax[2])*0.5);
      sceneTransform = Transformation(CGAL::SCALING, 2.0/d) 
                      *Transformation(CGAL::TRANSLATION, Point(0,0,0)-c)
                      *rotationComponent(sceneTransform);
   }
   glDraw();
}

/// private slot
void OpenGLFrame::newSkelJoint() {
   
   if (model->skeleton == 0)
      model->skeleton = new Skeleton;
   
   const Point &p = pNewJoinSource;
   idSelected = model->skeleton->newJoint(Point3(p[0],p[1],p[2]));
   
   //~ newSkelBoneSourceJoint(); // prepares the interaction
   //~ pressedbutton = 0x0;
   //~ skelEditOperation = JOINMOVE;
}

/// private slot
void OpenGLFrame::moveSkelJoint() {
   //~ pressedbutton = 0x0;
   skelEditOperation = JOINMOVE;
}

/// private slot
void OpenGLFrame::deleteSkelJoint() {
   
   model->skeleton->deleteJoint(idSelected);
   //~ pressedbutton = 0x0;
   glDraw();
}

/// private slot
void OpenGLFrame::newSkelBoneSourceJoint() {
   
   idNewJoinSource = idSelected;
   Point3 p = model->skeleton->getJointPosition(idNewJoinSource);
   pNewJoinSource = Point(p[0],p[1],p[2]);
   pNewJoinTarget = Point(p[0],p[1],p[2]);
   //~ pressedbutton = 0x0;
   //~ //pressedbutton |= LEFTBUTTONPRESS;
   skelEditOperation = BONENEW;
   interactionMode = EDITSKEL_MODE;
   glDraw();
}

/// private slot
void OpenGLFrame::splitSkelBone() {
   
   model->skeleton->splitBone(idSelected);
   //~ pressedbutton = 0x0;
}

/// private slot
void OpenGLFrame::deleteSkelBone() {
   
   model->skeleton->deleteBone(idSelected);
   //~ pressedbutton = 0x0;
}

/// private slot
void OpenGLFrame::flipSkelBone() {
   model->skeleton->flipBone(idSelected);
   //~ pressedbutton = 0x0;
}

void OpenGLFrame::newSkelBone(int idJoint, int idJointPrev) {
   
   model->skeleton->newBone(idJoint, idJointPrev);
   //~ interactionMode = SELECT_MODE;
   updateGL();
}

void OpenGLFrame::meshClustering(unsigned int k) {
   
   if (model != 0) model->clusterKcenters(k);
}

void OpenGLFrame::meshClustering(bool usingbones) {
   
   if (model == 0) return;
   if (model->skeleton == 0) return;
   
   Clustering clusterer(model);
   if (usingbones) clusterer.execute();
   else            clusterer.execute_joints();
}
